/**
 *
 * \file
 * iSendIR Main Code.
 *
 */

/*
 *
 * Copyright (c) 2010, Jesper Hansen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holders nor the names of any contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 */




#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "inc/hw_ssi.h"
#include "driverlib/ethernet.h"
#include "driverlib/flash.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"
#include "driverlib/ssi.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "utils/lwiplib.h"
#include "utils/swupdate.h"
#include "utils/uartstdio.h"
#include "utils/ustdlib.h"
#include "utils/softeeprom.h"
#include "httpserver_raw/httpd.h"
#include "fatfs/src/ff.h"
#include "fatfs/src/diskio.h"


#include <stdint.h>
#include <string.h>

#include "isendir.h"

#include "lmi_fs.h"

#include "telnet.h"
#include "receiver.h"

#include "ir.h"
#include "timeout.h"

#include "cgifuncs.h"
#include "cgissi.h"
#include "beacon.h"



//
// configuration data, this needs to go into persistent storage
//

network_settings_t nw_settings;
infrared_settings_t ir_settings;


//*****************************************************************************
//
//! \mainpage iSendIR Documentation
//! <h1>Ethernet to Infrared gateway</h1>
//!
//! This application convert ethernet commands into bitstreams and serial data.
//! These bitstreams can be routed either to IR (Infrared) ports for sending
//! data to TV and video equiment, or to I/O ports for datatransfer via a
//! RF link to remotecontrolled power switches and dimmers.
//! A web-server is implemented for configuring various options.
//! The Ethernet controller is using the lwIP TCP/IP Stack.  DHCP is used to
//! obtain an Ethernet address. If DHCP times out without obtaining an address,
//! the system will fallback to a preconfigured default IP.
//!
//! The file system code will first check to see if an SD card has been plugged
//! into the microSD slot.  If so, all file requests from the web server will
//! be directed to the SD card.  Otherwise, a default set of pages served up
//! by an internal file system will be used.
//!
//! For additional details on lwIP, refer to the lwIP web page at:
//! http://savannah.nongnu.org/projects/lwip/
//!
//! For additional details on FatFS, the filesystem used for the SD-card,
//! refer to the FatFS web page at:
//! http://elm-chan.org/fsw/ff/00index_e.html
//
//*****************************************************************************

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, unsigned long ulLine)
{
}
#endif



//*****************************************************************************
//!
//! This is the code that gets called when the processor enters a fault exception.
//! This function just setup the stack for easy access from a C-function and
//! call the common fault handler.
//!
//*****************************************************************************
void Fault_IntHandler(void)
{
	__asm (
		"TST LR, #4\n"
		"ITE EQ\n"
		"MRSEQ R0, MSP\n"
		"MRSNE R0, PSP\n"
		"B Fault_Handler_c\n"
	);
}


//*****************************************************************************
//!
//! This Fault Handler simply dumps status, register and stack information on
//! the serial debug console.
//! It then enters a permanent loop, flashing some LEDs.
//! \param fault_stack	A fault exception stack frame.
//!
//*****************************************************************************
void Fault_Handler_c(unsigned int * fault_stack)
{
	printf("\nHouston, we have a problem!\n");

	printf(" FAULTSTAT: %08x\n", *((unsigned int *) 0xe000ed28));
	printf("HFAULTSTAT: %08x\n", *((unsigned int *) 0xe000ed2c));
	printf("     MMADR: %08x\n", *((unsigned int *) 0xe000ed34));
	printf("  FAULTADR: %08x\n", *((unsigned int *) 0xe000ed38));
	printf("        R0: %08x\n", fault_stack[0]);
	printf("        R1: %08x\n", fault_stack[1]);
	printf("        R2: %08x\n", fault_stack[2]);
	printf("        R3: %08x\n", fault_stack[3]);
	printf("       R12: %08x\n", fault_stack[4]);
	printf("        LR: %08x\n", fault_stack[5]);
	printf("        PC: %08x\n", fault_stack[6]);
	printf("       PSR: %08x\n", fault_stack[7]);

    GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,GPIO_PIN_0);
    GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE,GPIO_PIN_0);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_0);

	while (1) {
		int i;
		GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 0);
		GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_0, 0);
		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_0, 0);

		for (i=0;i<500000;i++) __asm volatile ("nop");

		GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 1);
		GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_0, 1);
		GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_0, 1);

		for (i=0;i<500000;i++) __asm volatile ("nop");
	}
}


//*****************************************************************************
//!
//! Convert an IP address from unsigned long format to a string.
//! \param	pbuffer		Pointer to buffer where the string will be stored.
//!						Must have space for at least 16 characters.
//! \param	ip			IP address to convert.
//! \return				A pointer to the string in the supplied buffer.
//!
//*****************************************************************************
char *ip2string(char *pbuffer, unsigned long ip)
{
    unsigned char *p = (unsigned char *) &ip;
    usprintf(pbuffer, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
	return pbuffer;
}


//*****************************************************************************
//!
//! Fetch the system MAC from flash and convert to a string.
//! \param	pbuffer		Pointer to buffer where the string will be stored.
//!						Must have space for the given format.
//! \param	pformat		A format specifier for the MAC string. A typical format
//!						would be "02x-02x-02x-02x-02x-02x".
//! \return				A pointer to the string in the supplied buffer.
//!
//*****************************************************************************
char *mac2string(char *pbuffer, char *pformat)
{
	unsigned long ul0,ul1;
	unsigned char p[8];

    FlashUserGet(&ul0, &ul1);	// get MAC from flash

    p[0] = ((ul0 >>  0) & 0xff);
    p[1] = ((ul0 >>  8) & 0xff);
    p[2] = ((ul0 >> 16) & 0xff);
    p[3] = ((ul1 >>  0) & 0xff);
    p[4] = ((ul1 >>  8) & 0xff);
    p[5] = ((ul1 >> 16) & 0xff);

    usprintf(pbuffer, pformat,
    		p[0], p[1], p[2], p[3], p[4], p[5]);

	return pbuffer;
}


//*****************************************************************************
//!
//! Required by lwIP library to support any host-related timer functions.
//!
//*****************************************************************************
void lwIPHostTimerHandler(void)
{
    static unsigned long ulLastIPAddress = 0;
    unsigned long ulIPAddress;
    char buf[16];

    ulIPAddress = lwIPLocalIPAddrGet();

    if(ulIPAddress == 0)
    {
        printf("*");
    }
    else if (ulLastIPAddress != ulIPAddress)
    {
        ulLastIPAddress = ulIPAddress;
        printf("\nIP address: %s\n",ip2string(buf,ulIPAddress));
    }
}


//*****************************************************************************
//!
//! The interrupt handler for the SysTick interrupt.
//!
//*****************************************************************************
void SysTick_IntHandler(void)
{
    //
    // Call the lwIP timer handler.
    //
	lwIPTimer(SYSTICKMS);

    //
    // Run the file system tick handler.
    //
    fs_tick(SYSTICKMS);

    //
    // Run the timeout handler
    //
    timeout_tick(SYSTICKMS);
 }


//
//! Global flag for software update request
//
static volatile tBoolean g_bFirmwareUpdate = false;

//
//! Callback handler called when a Magic Packet has been sent to
//! indicate Software Update.
//! Set global flag to handle this outside interrupt context
//
void SoftwareUpdateRequestCallback(void)
{
	g_bFirmwareUpdate = true;
}



//*****************************************************************************
//!
//! Main entry point.
//!
//*****************************************************************************
int main(void)
{
    unsigned long ulUser0, ulUser1;
    unsigned char pucMACArray[8];

    //
    // Set the clocking to run from the PLL at 50 MHz
    //
    SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);

    //
    // Power-up all  GPIO
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);

    //
    // Initialize the UART for debug output.
    //
    GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_2 | GPIO_PIN_3);	// setup I/O pins for UART 1
    UARTStdioInit(1);											// enable UART1 for stdio use

    //
    // Say hello
    //
    printf("\n\niSendIR\nVersion " __FWVERSION__ "\n");
    printf("Built at " __DATE__ " " __TIME__ "\n");
    printf("Clocked at %d Hz\n",SysCtlClockGet());

    //
    // initialize SoftEEPROM functionality and setup EEPROM area
    //
    if (SoftEEPROMInit(SOFTEEPROM_START, SOFTEEPROM_END, SOFTEEPROM_END-SOFTEEPROM_START) != 0)
    	ERROR("SoftEEPROM Init failed\n");


    //test

    {
    	long result;
    	uint8_t id;
    	uint16_t data;
    	tBoolean found;

    	for (id=0;id<10;id++)
    	{
    		result = SoftEEPROMRead(id, &data, &found);
    		if (result == 0)
    		{
    			if (found)
    				printf("SoftEEPROM id %d = %d\n",id,data);
    			else
    				printf("SoftEEPROM id %d was not found\n",id);
    		}
    		else
    			ERROR("SoftEEPROM read failed for id %d\n",id)
    	}
    }

    //
    // Enable Port F for Ethernet LEDs.
    //  LED0        Bit 3   Output
    //  LED1        Bit 2   Output
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

    //
    // Temporarily use the pins as GPIO and blink the leds to show we're alive
    //
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_2);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_3);
    for (int i=0;i<5;i++)
    {
    	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3 | GPIO_PIN_2, GPIO_PIN_2);	// set 2, clear 3
    	delayms(100);
    	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3 | GPIO_PIN_2, GPIO_PIN_3);	// set 3, clear 2
    	delayms(100);
    }
	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3 | GPIO_PIN_2, GPIO_PIN_3 | GPIO_PIN_2);

    //
    // Change pins to ethernet use
    //
    GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3);

    //
    // Enable and Reset the Ethernet Controller.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
    SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);


    //
    // Configure SysTick for a periodic interrupt.
    //
    SysTickPeriodSet(SysCtlClockGet() / SYSTICKHZ);
    SysTickEnable();
    SysTickIntEnable();

    //
    // Enable processor interrupts.
    //
    IntMasterEnable();

    //
    // Initialize the file system.
    //
    fs_init();


    //! \todo we need to be able to setup the MAC in some way
    // for now, set it manually to a fixed value

// 00-1a-b6-00-06-d9
//               221100      554433
FlashUserSet(0x00b61a00, 0x00d90600);

    //
    // Configure the hardware MAC address for Ethernet Controller filtering of
    // incoming packets.
    //
    // For the iSendIR, the MAC address will be stored in the
    // non-volatile USER0 and USER1 registers.  These registers can be read
    // using the FlashUserGet function, as illustrated below.
    //
    FlashUserGet(&ulUser0, &ulUser1);
    if((ulUser0 == 0xffffffff) || (ulUser1 == 0xffffffff))
    {
        //
        // We should never get here.  This is an error if the MAC address has
        // not been programmed into the device.  Exit the program.
        //
    	ERROR("MAC Address not programmed!\n");
        while(1)
        {
        }
    }

    //
    // Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
    // address needed to program the hardware registers, then program the MAC
    // address into the Ethernet Controller registers.
    //
    pucMACArray[0] = ((ulUser0 >>  0) & 0xff);
    pucMACArray[1] = ((ulUser0 >>  8) & 0xff);
    pucMACArray[2] = ((ulUser0 >> 16) & 0xff);

    pucMACArray[3] = ((ulUser1 >>  0) & 0xff);
    pucMACArray[4] = ((ulUser1 >>  8) & 0xff);
    pucMACArray[5] = ((ulUser1 >> 16) & 0xff);

    printf("MAC: %02x-%02x-%02x-%02x-%02x-%02x\n",
		pucMACArray[0],	pucMACArray[1],	pucMACArray[2],
		pucMACArray[3],	pucMACArray[4],	pucMACArray[5] );

    //
    // Initialze the lwIP library, using DHCP.
    //
    lwIPInit(pucMACArray, 0, 0, 0, IPADDR_USE_DHCP);

    //
    // Start the remote software update module
    //
    SoftwareUpdateInit(SoftwareUpdateRequestCallback);

    //
    // Indicate that DHCP has started.
    //
    printf("Waiting for IP  ");

    //
    // Initialize a sample httpd server
    //
    httpd_init();

    //
    // Initialize the CGI and SSI functionality
    //
    cgissi_init();

    //
    // Initialize the telnet command interface
    //
    telnet_init();

    //
    // Initialize command handler
    //
    receiver_init();

    //
    // Initialize IR and bitstreaming
    //
    ir_init();

    //
    // Set the interrupt priorities.  We set the SysTick interrupt to a higher
    // priority than the Ethernet interrupt to ensure that the file system
    // tick is processed if SysTick occurs while the Ethernet handler is being
    // processed.  This is very likely since all the TCP/IP and HTTP work is
    // done in the context of the Ethernet interrupt.
    //
    IntPriorityGroupingSet(4);
    IntPrioritySet(INT_ETH, ETHERNET_INT_PRIORITY);
    IntPrioritySet(FAULT_SYSTICK, SYSTICK_INT_PRIORITY);

    //
    // wait for an IP
   	while (lwIPLocalIPAddrGet() == 0) {
   		// do nuthin'
   	}

  	//
  	// configure the identification beacon
  	//
  	beacon_config();

   	//
   	// enter main loop
   	//
   	printf("\nmain loop\n");

/*
   	{
   		long result;
   		uint8_t id;
   		for (id=0;id<5;id++) {
   			uint16_t val = g_ticker & 0xffff;
   			result = SoftEEPROMWrite(id, val);
   			if (result == 0) {
   				printf("SoftEEPROM id %d written with %d\n",id,val);
   			} else
    			ERROR("SoftEEPROM write failed for id %d\n",id)
   		}
   	}
*/

   	while (!g_bFirmwareUpdate)
    {

    	if (g_flags & FLAG_BEACON)
    	{
       		g_flags &= ~FLAG_BEACON;
    		beacon();
       	}

    	//
    	// If a network change have been requested, either from the web-page
    	// or through the set_net command, apply the changes here
    	// Also reconfigure the beacon for the new beacon frequency
    	//
    	if (g_flags & FLAG_NETCONFIG)
    	{
       		g_flags &= ~FLAG_NETCONFIG;
    		lwIPNetworkConfigChange(nw_settings.ns_ipaddr,
    								nw_settings.ns_netmask,
    								nw_settings.ns_gwaddr,
    								nw_settings.ns_usedhcp ? IPADDR_USE_DHCP : IPADDR_USE_STATIC);
    		beacon_config();
    	}
    }

    //
    // If we fall through to here, a software update magic packet have been received
    // on the ethernet interface
   	//
    printf("Updating firmware...\n");
    delayms(100);

    //
    // Enter the bootloader and go into update mode
    //
    SoftwareUpdateBegin();
    return 0;

}
